/**
 *  \file I3Functor.h
 *  \brief Similar to a TFunctor.  In this case we can encapsulate function
 * pointers with i3 specific argument and return types.
*/
#ifndef I3_FUNCTOR_H
#define I3_FUNCTOR_H 1

#include "OCD_include.h"

extern "C" {
    #include "i3.h"
    #include "i3_client_api.h"
}

class I3Trigger;
class I3OCDStateInfo;


class I3Functor : public TFunctor {
public:
   
    /** Destructor */
    virtual ~I3Functor() {

    }
    
    virtual void operator()(I3Trigger*, cbk_packet* )=0;  // call using operator
    virtual void operator()() = 0;
};

/** 
  * This class is used to define callbacks involving i3 specific objects
  * as parameters.  We want to keep the I3 specific objects out of 
  * TObjectFunctor.  That is why we have this separate inherited class.
  */
template <class TClass> class I3ObjectFunctor:public I3Functor {

    /** An I3 trigger stored at the time this functor is created.
      */
    I3Trigger *storedTrig;

    /**
     * The stored i3 virtual node.  Only one of storedTrig and storedVN will be 
     * non-NULL
     */
    I3OCDStateInfo *storedVN;
    
    void (TClass::*fpt)(I3Trigger*, cbk_packet*);   // pointer to member function
    
    /** This is the function to be executed when the operator () is called 
      * on a functor created an I3Trigger as argument.
      * The stored trigger is passed as the sole argument.
      */
    void (TClass::*fpt2) (I3Trigger*);

    /**
     * This is the function to be executed when the functor object was constructed
     * with an I3OCDStateInfo.
     * The stored I3OCDStateInfo is passed as the sole argument.
     */
    void (TClass::*fpt_VN) (I3OCDStateInfo*);
    
    
    TClass* pt2Object;                  // pointer to object
public:

    I3ObjectFunctor(TClass* _pt2Object, void(TClass::*_fpt)(I3Trigger*, cbk_packet*)) {
        pt2Object = _pt2Object;  
        fpt=_fpt; 
        fpt2 = NULL;
	    fpt_VN = NULL;
	    storedVN = NULL;
        storedTrig = NULL;
        
    }

    /**
      * Creates a new functor associated with the specified object.
      * @param _pt2Object The object on which the function is to be invoked.
      * @param _fpt2 Pointer to a member funtion of the _pt2Object class.  This function takes
      * an I3Trigger* and returns void.
      * @param trig The trigger to be stored in the functor.  This stored trigger will be passed
      * to the function pointed to by _fpt when the functor is invoked.
      */
    I3ObjectFunctor(TClass* _pt2Object, void(TClass::*_fpt2)(I3Trigger*), I3Trigger* trig) {
        this->pt2Object = _pt2Object;
        this->storedTrig = trig;
	    this->storedVN = NULL;
        this->fpt2 = _fpt2;
	    this->fpt_VN = NULL;
        this->fpt = NULL;
    }
    
    /**
      * Creates a new functor associated with the specified object.
      * @param _pt2Object The object on which the function is to be invoked.
      * @param _fpt_VN Pointer to a member funtion of the _pt2Object class.  This function takes
      * an I3OCDStateInfo* and returns void.
      * @param vn The i3 VN to be stored in the functor.  This stored i3VN will be passed
      * to the function pointed to by _fpt when the functor is invoked.
      */
    I3ObjectFunctor(TClass* _pt2Object, void(TClass::*_fpt_VN)(I3OCDStateInfo*), I3OCDStateInfo* vn) {
        this->pt2Object = _pt2Object;
        this->storedTrig = NULL;
	    this->storedVN = vn;

        this->fpt2 = NULL;
	    this->fpt_VN = _fpt_VN;
        this->fpt = NULL;
    }
    
    virtual void operator()() {

	    if (storedTrig != NULL) {
            (*pt2Object.*fpt2)(storedTrig);
	    } else if (storedVN != NULL) {
            if (fpt_VN == NULL) {
                printf("FPT_VN IS NULL\n");
            }
            (*pt2Object.*fpt_VN)(storedVN);
	    }
    }
    
    virtual void operator()(I3Trigger *trig, cbk_packet* pkt) {
        (*pt2Object.*fpt)(trig, pkt); // execute member function
    }

    /**
      * This does not do anything.  Just implemented due to inheritance requirements.
      */
    virtual void operator()(char* buf) {
    }

    /** 
      * Destructor
      */
    virtual  ~I3ObjectFunctor() {
    }
};

#endif
